🎖️GitЯра🎖️
Node / meshtastic / Meshtastic-Android / files / feature / firmware / src / commonMain / kotlin / org / meshtastic / feature / firmware / UsbUpdateSupport.kt
Displaying Raw • Download
feature/firmware/src/commonMain/kotlin/org/meshtastic/feature/firmware/UsbUpdateSupport.kt bd2863243bab6eb213401d949839a2bc74dde7e2 (bd286324) Text, 17.91 KB
T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.feature.firmware
Tff7b72import T7ee787co.touchlab.kermit.Logger
Tff7b72import T7ee787kotlinx.coroutines.CancellationException
Tff7b72import T7ee787kotlinx.coroutines.delay
Tff7b72import T7ee787org.meshtastic.core.common.util.CommonUri
Tff7b72import T7ee787org.meshtastic.core.common.util.safeCatching
Tff7b72import T7ee787org.meshtastic.core.database.entity.FirmwareRelease
Tff7b72import T7ee787org.meshtastic.core.model.DeviceHardware
Tff7b72import T7ee787org.meshtastic.core.repository.MaintenanceUf2Repository
Tff7b72import T7ee787org.meshtastic.core.repository.NodeRepository
Tff7b72import T7ee787org.meshtastic.core.repository.RadioController
Tff7b72import T7ee787org.meshtastic.core.resources.Res
Tff7b72import T7ee787org.meshtastic.core.resources.UiText
Tff7b72import T7ee787org.meshtastic.core.resources.firmware_update_copying
Tff7b72import T7ee787org.meshtastic.core.resources.firmware_update_downloading_percent
Tff7b72import T7ee787org.meshtastic.core.resources.firmware_update_flashing
Tff7b72import T7ee787org.meshtastic.core.resources.firmware_update_rebooting
Tff7b72import T7ee787org.meshtastic.core.resources.firmware_update_retrieval_failed
Tff7b72import T7ee787org.meshtastic.core.resources.firmware_update_usb_failed
Tff7b72import T7ee787org.meshtastic.core.resources.getStringSuspend
Tff7b72private Tff7b72const Tff7b72val Te6edf3USB_REBOOT_DELAY Tff7b72= T79c0ff5T79c0ff0T79c0ff0T79c0ff0L
Tff7b72private Tff7b72const Tff7b72val Te6edf3PERCENT_MAX Tff7b72= T79c0ff1T79c0ff0T79c0ff0
T8b949e/**
* One leg of a multi-pass USB/UF2 sequence.
*
* Two shapes because the passes differ in when their image becomes knowable: the application image is downloaded and
* verified up front, while an erase or bootloader image is chosen from what the mounted volume reports about itself, so
* it cannot exist until the user has pointed at the drive.
*/
Tff7b72internal Tff7b72sealed Tff7b72interface T56d364UsbFileSavePass Tb4b4b4{
Tff7b72val Te6edf3stepTb4b4b4: Te6edf3UsbFileSaveStep
T8b949e/** Image identity is decided from the mounted volume; nothing has been downloaded yet. */
Tff7b72data Tff7b72class T56d364FromVolumeTb4b4b4(
Tff7b72override Tff7b72val Te6edf3stepTb4b4b4: Te6edf3UsbFileSaveStepTb4b4b4,
Tff7b72val Te6edf3requestTb4b4b4: Te6edf3UsbMaintenanceRequestTb4b4b4,
T8b949e/**
* True for the nRF factory-erase image, which blocks on `while (!Serial)` before formatting and needs a host to
* assert DTR before it will run.
*/
Tff7b72val Te6edf3requiresCdcUnblockTb4b4b4: Tffa657BooleanTb4b4b4,
Tb4b4b4) Tb4b4b4: Te6edf3UsbFileSavePass
T8b949e/** Already downloaded and verified. */
Tff7b72data Tff7b72class T56d364PreparedTb4b4b4(Tff7b72override Tff7b72val Te6edf3stepTb4b4b4: Te6edf3UsbFileSaveStepTb4b4b4, Tff7b72val Te6edf3artifactTb4b4b4: Te6edf3FirmwareArtifactTb4b4b4, Tff7b72val Te6edf3fileNameTb4b4b4: Tffa657StringTb4b4b4) Tb4b4b4:
Te6edf3UsbFileSavePass
Tb4b4b4}
T8b949e/**
* Prepares a USB maintenance sequence: fetch the application image, reboot into the bootloader, and publish the first
* pass.
*
* Ordering is deliberate. The **application** image is downloaded and verified *before* `rebootToDfu`, because it is
* what restores the device after a destructive write — we never erase without already holding the firmware to put back.
* The **maintenance** image cannot be fetched this early: erase and bootloader images are selected from the Board-ID
* and SoftDevice the mounted volume reports, and no volume exists until the device has rebooted. It is fetched later,
* after the volume is vetted but still before anything is written, so a failure there costs a message rather than a
* device.
*
* @return The ordered passes, or an empty list when preparation failed (state has already been set to an error).
*/
Tff7b72internal Tff7b72suspend Tff7b72fun Td2a8ffperformUsbMaintenanceTb4b4b4(
Te6edf3requestTb4b4b4: Te6edf3UsbMaintenanceRequestTb4b4b4,
Te6edf3releaseTb4b4b4: Te6edf3FirmwareReleaseTb4b4b4,
Te6edf3hardwareTb4b4b4: Te6edf3DeviceHardwareTb4b4b4,
Te6edf3radioControllerTb4b4b4: Te6edf3RadioControllerTb4b4b4,
Te6edf3nodeRepositoryTb4b4b4: Te6edf3NodeRepositoryTb4b4b4,
Te6edf3updateStateTb4b4b4: Tb4b4b4(Te6edf3FirmwareUpdateStateTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Te6edf3retrieveUsbFirmwareTb4b4b4: Te6edf3suspend Tb4b4b4(Te6edf3FirmwareReleaseTb4b4b4, Te6edf3DeviceHardwareTb4b4b4, Tb4b4b4(Tffa657FloatTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4) Tff7b72-Tff7b72> Te6edf3FirmwareArtifact?Tb4b4b4,
Tb4b4b4)Tb4b4b4: Te6edf3ListTff7b72<Te6edf3UsbFileSavePassTff7b72> Tb4b4b4{
Tff7b72val Te6edf3downloadingMsg Tff7b72= Te6edf3getStringSuspendTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3firmware_update_downloading_percentTb4b4b4, T79c0ff0Tb4b4b4)Tb4b4b4.Te6edf3stripFormatArgsTb4b4b4(Tb4b4b4)
Te6edf3updateStateTb4b4b4(
Te6edf3FirmwareUpdateStateTb4b4b4.Te6edf3DownloadingTb4b4b4(Te6edf3ProgressStateTb4b4b4(Te6edf3message Tff7b72= Te6edf3UiTextTb4b4b4.Te6edf3DynamicStringTb4b4b4(Te6edf3downloadingMsgTb4b4b4)Tb4b4b4, Te6edf3progress Tff7b72= T79c0ff0fTb4b4b4)Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72val Te6edf3firmware Tff7b72=
Tff7b72try Tb4b4b4{
Te6edf3retrieveUsbFirmwareTb4b4b4(Te6edf3releaseTb4b4b4, Te6edf3hardwareTb4b4b4) Tb4b4b4{ Te6edf3progress Tff7b72-Tff7b72>
Tff7b72val Te6edf3percent Tff7b72= Tb4b4b4(Te6edf3progress Tff7b72* Te6edf3PERCENT_MAXTb4b4b4)Tb4b4b4.Te6edf3toIntTb4b4b4(Tb4b4b4)
Te6edf3updateStateTb4b4b4(
Te6edf3FirmwareUpdateStateTb4b4b4.Te6edf3DownloadingTb4b4b4(
Te6edf3ProgressStateTb4b4b4(
Te6edf3message Tff7b72= Te6edf3UiTextTb4b4b4.Te6edf3DynamicStringTb4b4b4(Te6edf3downloadingMsgTb4b4b4)Tb4b4b4,
Te6edf3progress Tff7b72= Te6edf3progressTb4b4b4,
Te6edf3details Tff7b72= Ta5d6ff"Tffd700$Te6edf3percentTa5d6ff%Ta5d6ff"Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tb4b4b4} Tff7b72catch Tb4b4b4(Te6edf3eTb4b4b4: Te6edf3CancellationExceptionTb4b4b4) Tb4b4b4{
Tff7b72throw Te6edf3e
Tb4b4b4} Tff7b72catch Tb4b4b4(Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffTooGenericExceptionCaughtTa5d6ff"Tb4b4b4) Te6edf3eTb4b4b4: Te6edf3ExceptionTb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3eTb4b4b4(Te6edf3eTb4b4b4) Tb4b4b4{ Ta5d6ff"Ta5d6ffMaintenance firmware download failedTa5d6ff" Tb4b4b4}
Tff7b72null
Tb4b4b4}
Tff7b72if Tb4b4b4(Te6edf3firmware Tff7b72=Tff7b72= Tff7b72nullTb4b4b4) Tb4b4b4{
Te6edf3updateStateTb4b4b4(
Te6edf3FirmwareUpdateStateTb4b4b4.Te6edf3ErrorTb4b4b4(
Te6edf3UiTextTb4b4b4.Te6edf3DynamicStringTb4b4b4(Te6edf3getStringSuspendTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3firmware_update_retrieval_failedTb4b4b4)Tb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72return Te6edf3emptyListTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tff7b72val Te6edf3maintenanceStep Tff7b72=
Tff7b72when Tb4b4b4(Te6edf3requestTb4b4b4) Tb4b4b4{
Te6edf3UsbMaintenanceRequestTb4b4b4.Te6edf3FactoryErase Tff7b72-Tff7b72> Te6edf3UsbFileSaveStepTb4b4b4.Te6edf3FactoryErase
Te6edf3UsbMaintenanceRequestTb4b4b4.Te6edf3BootloaderUpgrade Tff7b72-Tff7b72> Te6edf3UsbFileSaveStepTb4b4b4.Te6edf3BootloaderUpgrade
Tb4b4b4}
Te6edf3updateStateTb4b4b4(Te6edf3FirmwareUpdateStateTb4b4b4.Te6edf3ProcessingTb4b4b4(Te6edf3ProgressStateTb4b4b4(Te6edf3UiTextTb4b4b4.Te6edf3ResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3firmware_update_rebootingTb4b4b4)Tb4b4b4)Tb4b4b4)Tb4b4b4)
Te6edf3radioControllerTb4b4b4.Te6edf3rebootToDfuTb4b4b4(Te6edf3nodeRepositoryTb4b4b4.Te6edf3myNodeInfoTb4b4b4.Te6edf3valueTff7b72?.Te6edf3myNodeNum Tff7b72?: T79c0ff0Tb4b4b4)
Te6edf3delayTb4b4b4(Te6edf3USB_REBOOT_DELAYTb4b4b4)
Tff7b72val Te6edf3passes Tff7b72=
Te6edf3listOfTb4b4b4(
Te6edf3UsbFileSavePassTb4b4b4.Te6edf3FromVolumeTb4b4b4(
Te6edf3step Tff7b72= Te6edf3maintenanceStepTb4b4b4,
Te6edf3request Tff7b72= Te6edf3requestTb4b4b4,
T8b949e// Only the nRF erase image blocks waiting for a CDC host; pico_erase runs on its own, and a bootloader
T8b949e// self-update is consumed by the bootloader itself.
Te6edf3requiresCdcUnblock Tff7b72= Te6edf3request Tff7b72=Tff7b72= Te6edf3UsbMaintenanceRequestTb4b4b4.Te6edf3FactoryErase Tff7b72&Tff7b72& Te6edf3hardwareTb4b4b4.Te6edf3isNrf52ArcTb4b4b4,
Tb4b4b4)Tb4b4b4,
Te6edf3UsbFileSavePassTb4b4b4.Te6edf3PreparedTb4b4b4(
Te6edf3step Tff7b72= Te6edf3UsbFileSaveStepTb4b4b4.Te6edf3FirmwareTb4b4b4,
Te6edf3artifact Tff7b72= Te6edf3firmwareTb4b4b4,
Te6edf3fileName Tff7b72= Te6edf3firmwareTb4b4b4.Te6edf3fileName Tff7b72?: Ta5d6ff"Ta5d6fffirmware.uf2Ta5d6ff"Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3updateStateTb4b4b4(Te6edf3passesTb4b4b4.Te6edf3firstTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3toAwaitingFileSaveTb4b4b4(Tb4b4b4)Tb4b4b4)
Tff7b72return Te6edf3passes
Tb4b4b4}
T8b949e/** Outcome of attempting one pass. Every failure is typed so the caller can re-offer the same pass with copy. */
Tff7b72internal Tff7b72sealed Tff7b72interface T56d364UsbPassResult Tb4b4b4{
Tff7b72data Tff7b72object T56d364Written Tb4b4b4: Te6edf3UsbPassResult
T8b949e/** Volume vetting or image selection said no. Nothing was written. */
Tff7b72data Tff7b72class T56d364RefusedTb4b4b4(Tff7b72val Te6edf3reasonTb4b4b4: Te6edf3UsbMaintenanceRefusalTb4b4b4) Tb4b4b4: Te6edf3UsbPassResult
Tff7b72data Tff7b72object T56d364ImageDownloadFailed Tb4b4b4: Te6edf3UsbPassResult
Tff7b72data Tff7b72object T56d364CopyFailed Tb4b4b4: Te6edf3UsbPassResult
T8b949e/** The image was copied but the destination is still readable, so it did not land on a bootloader volume. */
Tff7b72data Tff7b72object T56d364WriteDidNotLand Tb4b4b4: Te6edf3UsbPassResult
T8b949e/** The erase image was written but could not be unblocked, so it never ran. Nothing was destroyed. */
Tff7b72data Tff7b72object T56d364CdcUnblockFailed Tb4b4b4: Te6edf3UsbPassResult
Tb4b4b4}
Tff7b72private Tff7b72const Tff7b72val Te6edf3DETACH_TIMEOUT_MS Tff7b72= T79c0ff6T79c0ff0Te6edf3_000L
Tff7b72private Tff7b72const Tff7b72val Te6edf3POST_WRITE_SETTLE_MS Tff7b72= T79c0ff1Te6edf3_500L
Tff7b72private Tff7b72const Tff7b72val Te6edf3CDC_UNBLOCK_WAIT_MS Tff7b72= T79c0ff1T79c0ff5Te6edf3_000L
Tff7b72private Tff7b72const Tff7b72val Te6edf3CDC_DTR_HOLD_MS Tff7b72= T79c0ff2Te6edf3_000L
T8b949e/**
* Writes one leg of a USB/UF2 sequence to a user-picked volume.
*
* Dependencies are constructor-injected rather than threaded through the call so the write itself keeps a short
* signature, and [unblockCdc] is a lambda so the serial plumbing can land separately from this decision logic.
*
* @property unblockCdc Opens the erase firmware's CDC port with DTR asserted so it stops waiting and formats. Returns
* false when no port could be claimed.
*/
Tff7b72internal Tff7b72class T56d364UsbPassWriterTb4b4b4(
Tff7b72private Tff7b72val Te6edf3fileHandlerTb4b4b4: Te6edf3FirmwareFileHandlerTb4b4b4,
Tff7b72private Tff7b72val Te6edf3maintenanceUf2RepositoryTb4b4b4: Te6edf3MaintenanceUf2RepositoryTb4b4b4,
Tff7b72private Tff7b72val Te6edf3retrieveMaintenanceUf2Tb4b4b4: Te6edf3suspend Tb4b4b4(Te6edf3MaintenanceUf2Tb4b4b4, Tb4b4b4(Tffa657FloatTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4) Tff7b72-Tff7b72> Te6edf3FirmwareArtifact?Tb4b4b4,
Tff7b72private Tff7b72val Te6edf3awaitDeviceDetachTb4b4b4: Te6edf3suspend Tb4b4b4(Tffa657LongTb4b4b4) Tff7b72-Tff7b72> Tffa657BooleanTb4b4b4,
Tff7b72private Tff7b72val Te6edf3unblockCdcTb4b4b4: Te6edf3suspend Tb4b4b4(Te6edf3waitMillisTb4b4b4: Tffa657LongTb4b4b4, Te6edf3holdMillisTb4b4b4: Tffa657LongTb4b4b4) Tff7b72-Tff7b72> Tffa657BooleanTb4b4b4,
Tb4b4b4) Tb4b4b4{
T8b949e/**
* Vets [treeUri], resolves the image if this pass needs it, writes, and confirms the write landed.
*
* Nothing is written until the volume has proved it is a UF2 bootloader drive and an image has been chosen and
* verified, so any refusal reaches the user with the device untouched.
*/
Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffReturnCountTa5d6ff"Tb4b4b4)
Tff7b72suspend Tff7b72fun Td2a8ffwriteTb4b4b4(
Te6edf3passTb4b4b4: Te6edf3UsbFileSavePassTb4b4b4,
Te6edf3treeUriTb4b4b4: Te6edf3CommonUriTb4b4b4,
Te6edf3hardwareTb4b4b4: Te6edf3DeviceHardwareTb4b4b4,
Te6edf3updateStateTb4b4b4: Tb4b4b4(Te6edf3FirmwareUpdateStateTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Tb4b4b4)Tb4b4b4: Te6edf3UsbPassResult Tb4b4b4{
Tff7b72val Te6edf3inspection Tff7b72= Te6edf3inspectMaintenanceVolumeTb4b4b4(Te6edf3treeUriTb4b4b4, Te6edf3fileHandlerTb4b4b4)
Tff7b72val Te6edf3volume Tff7b72=
Tff7b72when Tb4b4b4(Te6edf3inspectionTb4b4b4) Tb4b4b4{
Tff7b72is Te6edf3VolumeInspectionTb4b4b4.Te6edf3Rejected Tff7b72-Tff7b72> Tff7b72return Te6edf3UsbPassResultTb4b4b4.Te6edf3RefusedTb4b4b4(Te6edf3inspectionTb4b4b4.Te6edf3reasonTb4b4b4)
Tff7b72is Te6edf3VolumeInspectionTb4b4b4.Te6edf3Accepted Tff7b72-Tff7b72> Te6edf3inspectionTb4b4b4.Te6edf3volume
Tb4b4b4}
Tff7b72val Te6edf3artifact Tff7b72=
Tff7b72when Tb4b4b4(Tff7b72val Te6edf3resolved Tff7b72= Te6edf3resolveImageTb4b4b4(Te6edf3passTb4b4b4, Te6edf3hardwareTb4b4b4, Te6edf3volumeTb4b4b4, Te6edf3updateStateTb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72is Te6edf3ImageResolutionTb4b4b4.Te6edf3Failed Tff7b72-Tff7b72> Tff7b72return Te6edf3resolvedTb4b4b4.Te6edf3result
Tff7b72is Te6edf3ImageResolutionTb4b4b4.Te6edf3Ready Tff7b72-Tff7b72> Te6edf3resolvedTb4b4b4.Te6edf3artifact
Tb4b4b4}
Tff7b72val Te6edf3fileName Tff7b72= Te6edf3artifactTb4b4b4.Te6edf3fileName Tff7b72?: Tff7b72return Te6edf3UsbPassResultTb4b4b4.Te6edf3CopyFailed
Te6edf3updateStateTb4b4b4(Te6edf3FirmwareUpdateStateTb4b4b4.Te6edf3ProcessingTb4b4b4(Te6edf3ProgressStateTb4b4b4(Te6edf3UiTextTb4b4b4.Te6edf3ResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3firmware_update_copyingTb4b4b4)Tb4b4b4)Tb4b4b4)Tb4b4b4)
Tff7b72val Te6edf3destination Tff7b72= Te6edf3fileHandlerTb4b4b4.Te6edf3createDocumentInTreeTb4b4b4(Te6edf3treeUriTb4b4b4, Te6edf3fileNameTb4b4b4, Te6edf3UF2_MIME_TYPETb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3destination Tff7b72=Tff7b72= Tff7b72nullTb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3w Tb4b4b4{ Ta5d6ff"Ta5d6ffCould not create Tffd700$Te6edf3fileNameTa5d6ff on the picked volumeTa5d6ff" Tb4b4b4}
Tff7b72return Te6edf3UsbPassResultTb4b4b4.Te6edf3CopyFailed
Tb4b4b4}
Tff7b72val Te6edf3copied Tff7b72=
Te6edf3safeCatching Tb4b4b4{ Te6edf3fileHandlerTb4b4b4.Te6edf3copyToUriTb4b4b4(Te6edf3artifactTb4b4b4, Te6edf3destinationTb4b4b4) Tb4b4b4}
Tb4b4b4.Te6edf3onFailure Tb4b4b4{ Te6edf3LoggerTb4b4b4.Te6edf3eTb4b4b4(Tffa657itTb4b4b4) Tb4b4b4{ Ta5d6ff"Ta5d6ffCopying Tffd700$Te6edf3fileNameTa5d6ff to the UF2 volume failedTa5d6ff" Tb4b4b4} Tb4b4b4}
Tb4b4b4.Te6edf3getOrNullTb4b4b4(Tb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3copied Tff7b72=Tff7b72= Tff7b72nullTb4b4b4) Tff7b72return Te6edf3UsbPassResultTb4b4b4.Te6edf3CopyFailed
Te6edf3updateStateTb4b4b4(Te6edf3FirmwareUpdateStateTb4b4b4.Te6edf3ProcessingTb4b4b4(Te6edf3ProgressStateTb4b4b4(Te6edf3UiTextTb4b4b4.Te6edf3ResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3firmware_update_flashingTb4b4b4)Tb4b4b4)Tb4b4b4)Tb4b4b4)
T8b949e// A UF2 bootloader consumes the image and reboots, so the volume vanishing is the success signal. Missing the
T8b949e// broadcast is common and harmless — the read-back below is the authority either way.
Tff7b72val Te6edf3detached Tff7b72= Te6edf3awaitDeviceDetachTb4b4b4(Te6edf3DETACH_TIMEOUT_MSTb4b4b4)
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3detachedTb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3d Tb4b4b4{ Ta5d6ff"Ta5d6ffNo detach observed for Tffd700$Te6edf3fileNameTa5d6ff; falling back to the read-back checkTa5d6ff" Tb4b4b4}
Te6edf3delayTb4b4b4(Te6edf3POST_WRITE_SETTLE_MSTb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3fileHandlerTb4b4b4.Te6edf3isDestinationReadableTb4b4b4(Te6edf3destinationTb4b4b4)Tb4b4b4) Tb4b4b4{
T8b949e// Still readable means the bytes are sitting somewhere persistent, i.e. this was never a UF2 volume.
Te6edf3LoggerTb4b4b4.Te6edf3w Tb4b4b4{ Ta5d6ff"Tffd700$Te6edf3fileNameTa5d6ff is still readable after writing — it did not land on a bootloader volumeTa5d6ff" Tb4b4b4}
Tff7b72return Te6edf3UsbPassResultTb4b4b4.Te6edf3WriteDidNotLand
Tb4b4b4}
Tb4b4b4}
Tff7b72if Tb4b4b4(Te6edf3pass Tff7b72is Te6edf3UsbFileSavePassTb4b4b4.Te6edf3FromVolume Tff7b72&Tff7b72& Te6edf3passTb4b4b4.Te6edf3requiresCdcUnblockTb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3unblockCdcTb4b4b4(Te6edf3CDC_UNBLOCK_WAIT_MSTb4b4b4, Te6edf3CDC_DTR_HOLD_MSTb4b4b4)Tb4b4b4) Tb4b4b4{
T8b949e// The erase image blocks before InternalFS.format(), so a failed unblock has destroyed nothing.
Tff7b72return Te6edf3UsbPassResultTb4b4b4.Te6edf3CdcUnblockFailed
Tb4b4b4}
Tb4b4b4}
Tff7b72return Te6edf3UsbPassResultTb4b4b4.Te6edf3Written
Tb4b4b4}
T8b949e/** Either the image to write, or the result to return instead. */
Tff7b72private Tff7b72sealed Tff7b72interface T56d364ImageResolution Tb4b4b4{
Tff7b72data Tff7b72class T56d364ReadyTb4b4b4(Tff7b72val Te6edf3artifactTb4b4b4: Te6edf3FirmwareArtifactTb4b4b4) Tb4b4b4: Te6edf3ImageResolution
Tff7b72data Tff7b72class T56d364FailedTb4b4b4(Tff7b72val Te6edf3resultTb4b4b4: Te6edf3UsbPassResultTb4b4b4) Tb4b4b4: Te6edf3ImageResolution
Tb4b4b4}
T8b949e/**
* Produces the image for [pass]: already downloaded for the application pass, or chosen from what [volume] reports
* and fetched now for a maintenance pass.
*/
Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffresolveImageTb4b4b4(
Te6edf3passTb4b4b4: Te6edf3UsbFileSavePassTb4b4b4,
Te6edf3hardwareTb4b4b4: Te6edf3DeviceHardwareTb4b4b4,
Te6edf3volumeTb4b4b4: Te6edf3MaintenanceVolumeTb4b4b4,
Te6edf3updateStateTb4b4b4: Tb4b4b4(Te6edf3FirmwareUpdateStateTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Tb4b4b4)Tb4b4b4: Te6edf3ImageResolution Tff7b72= Tff7b72when Tb4b4b4(Te6edf3passTb4b4b4) Tb4b4b4{
Tff7b72is Te6edf3UsbFileSavePassTb4b4b4.Te6edf3Prepared Tff7b72-Tff7b72> Te6edf3ImageResolutionTb4b4b4.Te6edf3ReadyTb4b4b4(Te6edf3passTb4b4b4.Te6edf3artifactTb4b4b4)
Tff7b72is Te6edf3UsbFileSavePassTb4b4b4.Te6edf3FromVolume Tff7b72-Tff7b72>
Tff7b72when Tb4b4b4(
Tff7b72val Te6edf3choice Tff7b72=
Te6edf3chooseMaintenanceImageTb4b4b4(Te6edf3maintenanceUf2RepositoryTb4b4b4.Te6edf3getSnapshotTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3passTb4b4b4.Te6edf3requestTb4b4b4, Te6edf3hardwareTb4b4b4, Te6edf3volumeTb4b4b4)
Tb4b4b4) Tb4b4b4{
Tff7b72is Te6edf3MaintenanceImageChoiceTb4b4b4.Te6edf3Refused Tff7b72-Tff7b72> Te6edf3ImageResolutionTb4b4b4.Te6edf3FailedTb4b4b4(Te6edf3UsbPassResultTb4b4b4.Te6edf3RefusedTb4b4b4(Te6edf3choiceTb4b4b4.Te6edf3reasonTb4b4b4)Tb4b4b4)
Tff7b72is Te6edf3MaintenanceImageChoiceTb4b4b4.Te6edf3Resolved Tff7b72-Tff7b72> Tb4b4b4{
Tff7b72val Te6edf3downloadingMsg Tff7b72=
Te6edf3getStringSuspendTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3firmware_update_downloading_percentTb4b4b4, T79c0ff0Tb4b4b4)Tb4b4b4.Te6edf3stripFormatArgsTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3artifact Tff7b72=
Te6edf3retrieveMaintenanceUf2Tb4b4b4(Te6edf3choiceTb4b4b4.Te6edf3assetTb4b4b4) Tb4b4b4{ Te6edf3progress Tff7b72-Tff7b72>
Te6edf3updateStateTb4b4b4(
Te6edf3FirmwareUpdateStateTb4b4b4.Te6edf3DownloadingTb4b4b4(
Te6edf3ProgressStateTb4b4b4(
Te6edf3message Tff7b72= Te6edf3UiTextTb4b4b4.Te6edf3DynamicStringTb4b4b4(Te6edf3downloadingMsgTb4b4b4)Tb4b4b4,
Te6edf3progress Tff7b72= Te6edf3progressTb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tb4b4b4}
Te6edf3artifactTff7b72?.Te6edf3let Tb4b4b4{ Te6edf3ImageResolutionTb4b4b4.Te6edf3ReadyTb4b4b4(Tffa657itTb4b4b4) Tb4b4b4}
Tff7b72?: Te6edf3ImageResolutionTb4b4b4.Te6edf3FailedTb4b4b4(Te6edf3UsbPassResultTb4b4b4.Te6edf3ImageDownloadFailedTb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
T8b949e/** UF2 images are opaque binaries as far as the document provider is concerned. */
Tff7b72internal Tff7b72const Tff7b72val Te6edf3UF2_MIME_TYPE Tff7b72= Ta5d6ff"Ta5d6ffapplication/octet-streamTa5d6ff"
T8b949e/** The state that asks the user to point at the drive for this pass. */
Tff7b72internal Tff7b72fun Te6edf3UsbFileSavePassTb4b4b4.Td2a8fftoAwaitingFileSaveTb4b4b4(Te6edf3retryMessageTb4b4b4: Te6edf3UiText? Tff7b72= Tff7b72nullTb4b4b4)Tb4b4b4: Te6edf3FirmwareUpdateStateTb4b4b4.Te6edf3AwaitingFileSave Tff7b72=
Tff7b72when Tb4b4b4(Tff7b72thisTb4b4b4) Tb4b4b4{
Tff7b72is Te6edf3UsbFileSavePassTb4b4b4.Te6edf3FromVolume Tff7b72-Tff7b72>
Te6edf3FirmwareUpdateStateTb4b4b4.Te6edf3AwaitingFileSaveTb4b4b4(
Te6edf3uf2Artifact Tff7b72= Tff7b72nullTb4b4b4,
Te6edf3fileName Tff7b72= Tff7b72nullTb4b4b4,
Te6edf3step Tff7b72= Te6edf3stepTb4b4b4,
Te6edf3retryMessage Tff7b72= Te6edf3retryMessageTb4b4b4,
Tb4b4b4)
Tff7b72is Te6edf3UsbFileSavePassTb4b4b4.Te6edf3Prepared Tff7b72-Tff7b72>
Te6edf3FirmwareUpdateStateTb4b4b4.Te6edf3AwaitingFileSaveTb4b4b4(
Te6edf3uf2Artifact Tff7b72= Te6edf3artifactTb4b4b4,
Te6edf3fileName Tff7b72= Te6edf3fileNameTb4b4b4,
Te6edf3step Tff7b72= Te6edf3stepTb4b4b4,
Te6edf3retryMessage Tff7b72= Te6edf3retryMessageTb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffLongMethodTa5d6ff"Tb4b4b4)
Tff7b72internal Tff7b72suspend Tff7b72fun Td2a8ffperformUsbUpdateTb4b4b4(
Te6edf3releaseTb4b4b4: Te6edf3FirmwareReleaseTb4b4b4,
Te6edf3hardwareTb4b4b4: Te6edf3DeviceHardwareTb4b4b4,
Te6edf3firmwareUriTb4b4b4: Te6edf3CommonUri?Tb4b4b4,
Te6edf3radioControllerTb4b4b4: Te6edf3RadioControllerTb4b4b4,
Te6edf3nodeRepositoryTb4b4b4: Te6edf3NodeRepositoryTb4b4b4,
Te6edf3updateStateTb4b4b4: Tb4b4b4(Te6edf3FirmwareUpdateStateTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Te6edf3retrieveUsbFirmwareTb4b4b4: Te6edf3suspend Tb4b4b4(Te6edf3FirmwareReleaseTb4b4b4, Te6edf3DeviceHardwareTb4b4b4, Tb4b4b4(Tffa657FloatTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4) Tff7b72-Tff7b72> Te6edf3FirmwareArtifact?Tb4b4b4,
Tb4b4b4)Tb4b4b4: Te6edf3FirmwareArtifact? Tb4b4b4{
Tff7b72var Te6edf3cleanupArtifactTb4b4b4: Te6edf3FirmwareArtifact? Tff7b72= Tff7b72null
Tff7b72return Tff7b72try Tb4b4b4{
Tff7b72val Te6edf3downloadingMsg Tff7b72= Te6edf3getStringSuspendTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3firmware_update_downloading_percentTb4b4b4, T79c0ff0Tb4b4b4)Tb4b4b4.Te6edf3stripFormatArgsTb4b4b4(Tb4b4b4)
Te6edf3updateStateTb4b4b4(
Te6edf3FirmwareUpdateStateTb4b4b4.Te6edf3DownloadingTb4b4b4(
Te6edf3ProgressStateTb4b4b4(Te6edf3message Tff7b72= Te6edf3UiTextTb4b4b4.Te6edf3DynamicStringTb4b4b4(Te6edf3downloadingMsgTb4b4b4)Tb4b4b4, Te6edf3progress Tff7b72= T79c0ff0fTb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3firmwareUri Tff7b72!Tff7b72= Tff7b72nullTb4b4b4) Tb4b4b4{
Te6edf3updateStateTb4b4b4(
Te6edf3FirmwareUpdateStateTb4b4b4.Te6edf3ProcessingTb4b4b4(Te6edf3ProgressStateTb4b4b4(Te6edf3UiTextTb4b4b4.Te6edf3ResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3firmware_update_rebootingTb4b4b4)Tb4b4b4)Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72val Te6edf3myNodeNum Tff7b72= Te6edf3nodeRepositoryTb4b4b4.Te6edf3myNodeInfoTb4b4b4.Te6edf3valueTff7b72?.Te6edf3myNodeNum Tff7b72?: T79c0ff0
Te6edf3radioControllerTb4b4b4.Te6edf3rebootToDfuTb4b4b4(Te6edf3myNodeNumTb4b4b4)
Te6edf3delayTb4b4b4(Te6edf3USB_REBOOT_DELAYTb4b4b4)
Tff7b72val Te6edf3sourceArtifact Tff7b72=
Te6edf3FirmwareArtifactTb4b4b4(Te6edf3uri Tff7b72= Te6edf3firmwareUriTb4b4b4, Te6edf3fileName Tff7b72= Te6edf3firmwareUriTb4b4b4.Te6edf3pathSegmentsTb4b4b4.Te6edf3lastOrNullTb4b4b4(Tb4b4b4) Tff7b72?: Ta5d6ff"Ta5d6fffirmware.uf2Ta5d6ff"Tb4b4b4)
Te6edf3updateStateTb4b4b4(Te6edf3FirmwareUpdateStateTb4b4b4.Te6edf3AwaitingFileSaveTb4b4b4(Te6edf3sourceArtifactTb4b4b4, Te6edf3sourceArtifactTb4b4b4.Te6edf3fileName Tff7b72?: Ta5d6ff"Ta5d6fffirmware.uf2Ta5d6ff"Tb4b4b4)Tb4b4b4)
Tff7b72null
Tb4b4b4} Tff7b72else Tb4b4b4{
Tff7b72val Te6edf3firmwareFile Tff7b72=
Te6edf3retrieveUsbFirmwareTb4b4b4(Te6edf3releaseTb4b4b4, Te6edf3hardwareTb4b4b4) Tb4b4b4{ Te6edf3progress Tff7b72-Tff7b72>
Tff7b72val Te6edf3percent Tff7b72= Tb4b4b4(Te6edf3progress Tff7b72* Te6edf3PERCENT_MAXTb4b4b4)Tb4b4b4.Te6edf3toIntTb4b4b4(Tb4b4b4)
Te6edf3updateStateTb4b4b4(
Te6edf3FirmwareUpdateStateTb4b4b4.Te6edf3DownloadingTb4b4b4(
Te6edf3ProgressStateTb4b4b4(
Te6edf3message Tff7b72= Te6edf3UiTextTb4b4b4.Te6edf3DynamicStringTb4b4b4(Te6edf3downloadingMsgTb4b4b4)Tb4b4b4,
Te6edf3progress Tff7b72= Te6edf3progressTb4b4b4,
Te6edf3details Tff7b72= Ta5d6ff"Tffd700$Te6edf3percentTa5d6ff%Ta5d6ff"Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tb4b4b4}
Te6edf3cleanupArtifact Tff7b72= Te6edf3firmwareFile
Tff7b72if Tb4b4b4(Te6edf3firmwareFile Tff7b72=Tff7b72= Tff7b72nullTb4b4b4) Tb4b4b4{
Te6edf3updateStateTb4b4b4(
Te6edf3FirmwareUpdateStateTb4b4b4.Te6edf3ErrorTb4b4b4(
Te6edf3UiTextTb4b4b4.Te6edf3DynamicStringTb4b4b4(Te6edf3getStringSuspendTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3firmware_update_retrieval_failedTb4b4b4)Tb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72null
Tb4b4b4} Tff7b72else Tb4b4b4{
Tff7b72val Te6edf3processingState Tff7b72= Te6edf3ProgressStateTb4b4b4(Te6edf3UiTextTb4b4b4.Te6edf3ResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3firmware_update_rebootingTb4b4b4)Tb4b4b4)
Te6edf3updateStateTb4b4b4(Te6edf3FirmwareUpdateStateTb4b4b4.Te6edf3ProcessingTb4b4b4(Te6edf3processingStateTb4b4b4)Tb4b4b4)
Tff7b72val Te6edf3myNodeNum Tff7b72= Te6edf3nodeRepositoryTb4b4b4.Te6edf3myNodeInfoTb4b4b4.Te6edf3valueTff7b72?.Te6edf3myNodeNum Tff7b72?: T79c0ff0
Te6edf3radioControllerTb4b4b4.Te6edf3rebootToDfuTb4b4b4(Te6edf3myNodeNumTb4b4b4)
Te6edf3delayTb4b4b4(Te6edf3USB_REBOOT_DELAYTb4b4b4)
Tff7b72val Te6edf3fileName Tff7b72= Te6edf3firmwareFileTb4b4b4.Te6edf3fileName Tff7b72?: Ta5d6ff"Ta5d6fffirmware.uf2Ta5d6ff"
Tff7b72val Te6edf3fileSaveState Tff7b72= Te6edf3FirmwareUpdateStateTb4b4b4.Te6edf3AwaitingFileSaveTb4b4b4(Te6edf3firmwareFileTb4b4b4, Te6edf3fileNameTb4b4b4)
Te6edf3updateStateTb4b4b4(Te6edf3fileSaveStateTb4b4b4)
Te6edf3firmwareFile
Tb4b4b4}
Tb4b4b4}
Tb4b4b4} Tff7b72catch Tb4b4b4(Te6edf3eTb4b4b4: Te6edf3CancellationExceptionTb4b4b4) Tb4b4b4{
Tff7b72throw Te6edf3e
Tb4b4b4} Tff7b72catch Tb4b4b4(Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffTooGenericExceptionCaughtTa5d6ff"Tb4b4b4) Te6edf3eTb4b4b4: Te6edf3ExceptionTb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3eTb4b4b4(Te6edf3eTb4b4b4) Tb4b4b4{ Ta5d6ff"Ta5d6ffUSB Update failedTa5d6ff" Tb4b4b4}
Tff7b72val Te6edf3usbFailedMsg Tff7b72= Te6edf3getStringSuspendTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3firmware_update_usb_failedTb4b4b4)
Te6edf3updateStateTb4b4b4(Te6edf3FirmwareUpdateStateTb4b4b4.Te6edf3ErrorTb4b4b4(Te6edf3UiTextTb4b4b4.Te6edf3DynamicStringTb4b4b4(Te6edf3eTb4b4b4.Te6edf3message Tff7b72?: Te6edf3usbFailedMsgTb4b4b4)Tb4b4b4)Tb4b4b4)
Te6edf3cleanupArtifact
Tb4b4b4}
Tb4b4b4}
Served by rngit 1.5.1 - Generated in 0.11s